﻿//  Copyright (c) 2009 Ray Liang (http://www.dotnetage.com)
//  Dual licensed under the MIT and GPL licenses:
//  http://www.opensource.org/licenses/mit-license.php
//  http://www.gnu.org/licenses/gpl.html
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Text;
using System.IO;
using System.Web.UI;

namespace DNA.Mvc
{
    /// <summary>
    /// Provides the helper methods to build the unorder list elements for the INavigatabe object.
    /// </summary>
    public class NodeUIBuilder : IDisposable
    {
        private HtmlTextWriter writer;

        /// <summary>
        /// The delegate for render the node content.
        /// </summary>
        public event Func<INavigable,NodeUIBuilder,bool> ContentRender;

        /// <summary>
        /// Trigger on content render.
        /// </summary>
        /// <param name="node">The navigable node.</param>
        /// <returns></returns>
        protected bool OnContentRender(INavigable node)
        {
            if (ContentRender != null)
                return ContentRender(node,this);
            return true;
        }

        /// <summary>
        /// Gets the HtmlTextWriter.
        /// </summary>
        protected HtmlTextWriter Writer
        {
            get { return writer; }
        }

        private StringBuilder _stringBuilder;

        /// <summary>
        /// Construct the NodeUIBuilder
        /// </summary>
        public NodeUIBuilder()
        {
            _stringBuilder = new StringBuilder();
            StringWriter stringWriter = new StringWriter(_stringBuilder);
            writer = new HtmlTextWriter(stringWriter);
        }

        /// <summary>
        /// Write the a tag for specified node to result
        /// </summary>
        /// <param name="node">Specified the node instance to write</param>
        public NodeUIBuilder WriteBeginLink(INavigable node)
        {
            return WriteBeginLink(node, null);
        }

        /// <summary>
        /// Write the a tag and html attributes to result.
        /// </summary>
        /// <param name="node">Specified the node instance to write</param>
        /// <param name="htmlAttributes">The html attributs of the a tag.</param>
        public NodeUIBuilder WriteBeginLink(INavigable node, object htmlAttributes)
        {
            writer.WriteBeginTag("a");
            //if (node.NavigateUrl.StartsWith("javascript:"))
            //    writer.WriteAttribute("href", "javascript:void(0);");
            //else
                writer.WriteAttribute("href", node.NavigateUrl);

            if (!string.IsNullOrEmpty(node.Target))
                writer.WriteAttribute("target", node.Target);
            WriteAttributes(htmlAttributes);
            writer.Write(HtmlTextWriter.TagRightChar);
            return this;
        }

        /// <summary>
        /// Write the object as attributes to result.
        /// </summary>
        /// <param name="htmlAttributes">The html attributes object.</param>
        public NodeUIBuilder WriteAttributes(object htmlAttributes)
        {
            if (htmlAttributes != null)
            {
                var attributes = ObjectHelper.ConvertObjectToDictionary(htmlAttributes);
                foreach (var key in attributes.Keys)
                    writer.WriteAttribute(key, attributes[key] != null ? attributes[key].ToString() : "");
            }
            return this;
        }

        /// <summary>
        /// Write the hidden tag to result.
        /// </summary>
        /// <param name="value">Specified the value of the hidden input.</param>
        public NodeUIBuilder WriteHidden(string value)
        {
            writer.WriteBeginTag("input");
            writer.WriteAttribute("type", "hidden");
            writer.WriteAttribute("value", value);
            writer.Write(HtmlTextWriter.SelfClosingTagEnd);
            return this;
        }

        /// <summary>
        /// Write the img tag for specified node to result.
        /// </summary>
        /// <param name="node">Specified the node object to write.</param>
        public NodeUIBuilder WriteImage(INavigable node)
        {
            WriteImage(node, null);
            return this;
        }

        /// <summary>
        /// Write the img tag for specified node to result.
        /// </summary>
        /// <param name="node">Specified the node object to write.</param>
        /// <param name="htmlAttributes">The html attributes object for img tag</param>
        public NodeUIBuilder WriteImage(INavigable node, object htmlAttributes)
        {
            var _imgUrl = node.ImageUrl;
            if (!string.IsNullOrEmpty(_imgUrl))
            {
                writer.WriteBeginTag("img");
                writer.WriteAttribute("alt", "");
                if (VirtualPathUtility.IsAppRelative(_imgUrl))
                    writer.WriteAttribute("src", VirtualPathUtility.ToAbsolute(_imgUrl));
                else
                    writer.WriteAttribute("src", _imgUrl);
                WriteAttributes(htmlAttributes);
                writer.Write(HtmlTextWriter.SelfClosingTagEnd);
            }
            return this;
        }

        /// <summary>
        /// Write the node title to span tag.
        /// </summary>
        /// <param name="node">Specified the node object to write.</param>
        public NodeUIBuilder WirteTitle(INavigable node)
        {
            WriteTitle(node, null);
            return this;
        }

        /// <summary>
        /// Write the node title to span tag.
        /// </summary>
        /// <param name="node">Specified the node object to write.</param>
        /// <param name="htmlAttributes">The html attributs of the span tag</param>
        public NodeUIBuilder WriteTitle(INavigable node, object htmlAttributes)
        {
            writer.WriteBeginTag("span");
            writer.WriteAttribute("title", node.Description);
            WriteAttributes(htmlAttributes);
            writer.Write(HtmlTextWriter.TagRightChar);
            writer.Write(node.Title);
            writer.WriteEndTag("span");
            return this;
        }

        /// <summary>
        /// Write ">" to result.
        /// </summary>
        public NodeUIBuilder WriteTagRightChar()
        {
            writer.Write(HtmlTextWriter.SelfClosingTagEnd);
            return this;
        }

        /// <summary>
        /// Write the begin tag to result
        /// </summary>
        /// <param name="tagName">Specified the tag name.</param>
        /// <param name="htmlAttributes">Specified the attributes of the element</param>
        public NodeUIBuilder WriteBeginTag(string tagName, object htmlAttributes)
        {
            writer.WriteBeginTag(tagName);
            WriteAttributes(htmlAttributes);
            writer.Write(HtmlTextWriter.TagRightChar);
            return this;
        }

        public NodeUIBuilder WriteFullBeginTag(string tagName)
        {
            writer.WriteFullBeginTag(tagName);
            return this;
        }
        /// <summary>
        /// Write the end tag to result.
        /// </summary>
        /// <param name="tagName">Specified the tag name</param>
        public NodeUIBuilder WriteEndTag(string tagName)
        {
            writer.WriteEndTag(tagName);
            return this;
        }

        /// <summary>
        /// Write the node just have icon output element
        /// </summary>
        /// <param name="iconClass">The css class of the icon</param>
        public NodeUIBuilder WriteIconNode(string iconClass)
        {
            WriteIconNode(iconClass, null);
            return this;
        }

        /// <summary>
        /// Write the node just have icon output element
        /// </summary>
        /// <param name="iconClass">The css class of the icon</param>
        /// <param name="htmlAttribute">The html attributes of the icon element</param>
        public NodeUIBuilder WriteIconNode(string iconClass, object htmlAttribute)
        {
            writer.WriteBeginTag("li");
            if (htmlAttribute != null)
                WriteAttributes(ObjectHelper.ConvertObjectToDictionary(htmlAttribute));
            writer.Write(Html32TextWriter.TagRightChar);
            writer.WriteBeginTag("span");
            writer.WriteAttribute("class", iconClass);
            writer.Write(Html32TextWriter.TagRightChar);
            writer.WriteEndTag("span");
            writer.WriteEndTag("li");
            return this;
        }

        /// <summary>
        /// Write the node content to result.
        /// </summary>
        /// <param name="node">Specified the node object</param>
        /// <remarks>
        /// The result is in this format: 
        /// 
        /// &lt;a href=[node.NavigateUrl] target='[node.Target]'&gt;
        ///       &lt;input type='hidden' value='[node.Key]'&gt;
        ///       &lt;img src='[node.ImageUrl]'&gt;
        ///       &lt;span&gt;[node.Title]&lt;/span&gt;
        /// &lt;/a&gt;     
        /// 
        /// a,hidden,img element is optional. if the node property is null or empty the elements will not render.
        /// </remarks>
        public virtual NodeUIBuilder WriteNodeContent(INavigable node,object nodeHtmlAttributes)
        {
            if (OnContentRender(node))
            {
                if (!string.IsNullOrEmpty(node.NavigateUrl))
                    WriteBeginLink(node,nodeHtmlAttributes);

                if (node.Value != null)
                    WriteHidden(node.Value.ToString());

                if (!string.IsNullOrEmpty(node.ImageUrl))
                    WriteImage(node);

                WriteTitle(node, new { style = "padding:2px;cursor:default;display:inline-block;" });

                if (!string.IsNullOrEmpty(node.NavigateUrl))
                    WriteEndTag("a");
            }
            return this;
        }

        public virtual NodeUIBuilder WriteNodeContent(INavigable node)
        {
            return this.WriteNodeContent(node, null);
        }

        /// <summary>
        /// Write the node instance into html render result.
        /// </summary>
        /// <param name="node">Sepcified the node object</param>
        /// <remarks>
        /// The result is in this format: 
        ///        &lt;li>
        ///              &lt;a href=[node.NavigateUrl] target='[node.Target]'&gt;
        ///                    &lt;input type='hidden' value='[node.Key]'&gt;
        ///                    &lt;img src='[node.ImageUrl]'&gt;
        ///                    &lt;span>[node.Title]&lt;/span&gt;
        ///               &lt;/a>     
        ///        &lt;/li>
        /// a,hidden,img element is optional. if the node property is null or empty the elements will not render.
        /// </remarks>
        public virtual NodeUIBuilder WriteNode(INavigable node)
        {
            return WriteNode(node, new { style = "overflow:auto;", title = node.Description });

        }

        public virtual NodeUIBuilder WriteNode(INavigable node, object htmlAttributes)
        {
            WriteBeginTag("li", htmlAttributes);
            WriteNodeContent(node);
            writer.WriteEndTag("li");
            return this;
        }

        public NodeUIBuilder Write(object rawText)
        {
            writer.Write(rawText);
            return this;
        }

        /// <summary>
        /// Write the nodes into html render result.
        /// </summary>
        /// <remarks>
        /// The result is in this format: 
        ///     &lt;ul&gt;
        ///        &lt;li&gt;
        ///              &lt;a href=[node.NavigateUrl] target='[node.Target]'&gt;
        ///                    &lt;input type='hidden' value='[node.Key]'&gt;
        ///                    &lt;img src='[node.ImageUrl]'&gt;
        ///                    &lt;span>[node.Title]&lt;/span&gt;
        ///               &lt;/a>     
        ///        &lt;/li>
        ///        .
        ///        .
        ///        .
        ///      &lt;/ul> 
        /// a,hidden,img element is optional. if the node property is null or empty the elements will not render.
        /// </remarks>
        public virtual NodeUIBuilder WriteNodes(IEnumerable<INavigable> nodes,object htmlAttributes)
        {
            WriteBeginTag("ul",htmlAttributes);
            if (nodes != null)
            {
                foreach (var node in nodes)
                    WriteNode(node);
            }
            WriteEndTag("ul");
            return this;
        }

        public NodeUIBuilder WriteNodes(IEnumerable<INavigable> nodes)
        {
            this.WriteNodes(nodes, null);
            return this;
        }

        /// <summary>
        /// Get the result string.
        /// </summary>
        /// <returns>The string of the result</returns>
        public string ToString()
        {
            return _stringBuilder.ToString();
        }

        #region IDisposable 成员
        /// <summary>
        /// Resolve the resource.
        /// </summary>
        public void Dispose()
        {
            if (writer != null)
                writer.Dispose();
        }

        #endregion

    }

    
}
